#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include "RingQueue.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"

// V3版本：
// 功能：多人聊天室
// 核心：多线程模块、生产者消费者模型（包括守卫锁组件）

namespace ns_server
{
  const static uint16_t default_port = 8080; // 默认端口号

  class UdpServer
  {
  public:
    UdpServer(uint16_t port = default_port)
        : port_(port)
    {
      pthread_mutex_init(&lock_, nullptr);

      precv_ = new Thread(0, std::bind(&UdpServer::Recv, this));
      psend_ = new Thread(1, std::bind(&UdpServer::Broadcast, this));

      //守护进程化
      daemon(0, 0 || 1 || 2);
    }

    ~UdpServer()
    {
      psend_->join();
      precv_->join();

      pthread_mutex_destroy(&lock_);

      delete (psend_);
      delete (precv_);
    }

    void StartServer()
    {
      // 1、创建套接字
      sock_ = socket(AF_INET, SOCK_DGRAM, 0); // IPV4 标准的网络通信
      if (sock_ == -1)
      {
        std::cerr << "Create Socket Fail: " << strerror(errno) << std::endl;
        exit(SOCKET_ERR);
      }
      std::cout << "Create Socket Succese: " << sock_ << std::endl;

      // 2、绑定 IP地址 和 Port号
      struct sockaddr_in local;
      bzero(&local, sizeof(local));

      local.sin_family = AF_INET;    // PF_INET 也行
      local.sin_port = htons(port_); // 设置端口号(主机转网络)
      // local.sin_addr.s_addr = inet_addr(ip_.c_str()); //点分十进制字符串转为整型，整型序列转为网络序列
      local.sin_addr.s_addr = INADDR_ANY; // 绑定任何可用的 IP 地址

      if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
      {
        std::cerr << "Bind Socket Fail: " << strerror(errno) << std::endl;
        exit(BIND_ERR);
      }
      std::cout << "Bind Socket Succese: " << sock_ << std::endl;

      std::cout << inet_ntoa(local.sin_addr) << " : " << ntohs(local.sin_port) << std::endl;

      //启动线程
      precv_->run();
      psend_->run();
    }

    void addUser(const std::string name, struct sockaddr_in &peer)
    {
      LockGuard lg(&lock_);

      auto it = onlineUser_.find(name);
      if (it != onlineUser_.end())
        return;

      // 新加入用户
      onlineUser_.insert(std::make_pair(name, peer));
    }

    void Recv()
    {
      char buffer[1024] = {0}; // 缓冲区大小
      while (true)
      {
        // 收信息
        struct sockaddr_in peer; // 客户端的套接字信息
        socklen_t len = sizeof peer;
        int n = recvfrom(sock_, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
        if (n > 0) buffer[n] = '\0';
        else continue;

        // 提取客户端的信息
        std::string clientip = inet_ntoa(peer.sin_addr); // ip
        uint16_t clientport = ntohs(peer.sin_port);      // port
        std::cout << "client [" << clientip << ":" << clientport << "] $ " << buffer << std::endl;

        // 业务处理
        std::string name = clientip + "-" + std::to_string(clientport);

        addUser(name, peer); // 新增用户

        std::string msg = "[" + name + "]" + " >> " + buffer;
        rq_.push(msg); // 将信息装载至环形队列中
      }
    }

    void Broadcast()
    {
      // 广播消息
      while (true)
      {
        std::string msg;
        rq_.pop(&msg);

        // 将信息先存入 vector 中
        std::vector<struct sockaddr_in> vs;
        {
          LockGuard lg(&lock_);
          for (auto &e : onlineUser_)
            vs.push_back(e.second);
        }

        // 将信息进行广播
        for (auto user : vs)
          sendto(sock_, msg.c_str(), msg.size(), 0, (struct sockaddr *)&user, sizeof(user));
      }
    }

  private:
    int sock_;      // 套接字
    uint16_t port_; // 端口号

    std::unordered_map<std::string, struct sockaddr_in> onlineUser_; // 在线用户
    RingQueue<std::string> rq_;                                      // 生产者消费者模型
    pthread_mutex_t lock_;
    Thread *precv_; // 专门负责收消息
    Thread *psend_; // 专门负责发消息
  };
}